<template>
  <scroll-view 
    class="virtual-list"
    :scroll-y="true"
    :style="containerStyle"
    :scroll-top="scrollTop"
    :enable-back-to-top="enableBackToTop"
    @scroll="onScroll"
    @scrolltoupper="onScrollToUpper"
    @scrolltolower="onScrollToLower"
  >
    <!-- 上方占位 -->
    <view :style="{ height: topPlaceholderHeight + 'px' }"></view>
    
    <!-- 可见项目 -->
    <view class="virtual-list-item" v-for="item in visibleItems" :key="item.key">
      <slot :item="item.data" :index="item.index"></slot>
    </view>
    
    <!-- 下方占位 -->
    <view :style="{ height: bottomPlaceholderHeight + 'px' }"></view>
    
    <!-- 加载更多 -->
    <view v-if="showLoadMore" class="load-more">
      <u-loading-icon v-if="loading" mode="flower" />
      <text class="load-more-text">
        {{ loading ? '加载中...' : hasMore ? '上拉加载更多' : '没有更多数据了' }}
      </text>
    </view>
  </scroll-view>
</template>

<script>
export default {
  name: 'VirtualList',
  props: {
    // 数据列表
    items: {
      type: Array,
      default: () => []
    },
    // 每项高度
    itemHeight: {
      type: Number,
      required: true
    },
    // 容器高度
    height: {
      type: [String, Number],
      default: '100vh'
    },
    // 缓冲区项目数量
    bufferSize: {
      type: Number,
      default: 5
    },
    // 是否显示加载更多
    showLoadMore: {
      type: Boolean,
      default: true
    },
    // 是否正在加载
    loading: {
      type: Boolean,
      default: false
    },
    // 是否还有更多数据
    hasMore: {
      type: Boolean,
      default: true
    },
    // 是否启用回到顶部
    enableBackToTop: {
      type: Boolean,
      default: true
    },
    // 触发加载更多的距离
    lowerThreshold: {
      type: Number,
      default: 100
    }
  },
  
  data() {
    return {
      scrollTop: 0,
      containerHeight: 0,
      startIndex: 0,
      endIndex: 0
    }
  },
  
  computed: {
    containerStyle() {
      return {
        height: typeof this.height === 'number' ? this.height + 'px' : this.height
      }
    },
    
    // 可见项目数量
    visibleCount() {
      return Math.ceil(this.containerHeight / this.itemHeight) + this.bufferSize * 2
    },
    
    // 可见项目
    visibleItems() {
      return this.items.slice(this.startIndex, this.endIndex).map((item, index) => ({
        key: this.getItemKey(item, this.startIndex + index),
        data: item,
        index: this.startIndex + index
      }))
    },
    
    // 上方占位高度
    topPlaceholderHeight() {
      return this.startIndex * this.itemHeight
    },
    
    // 下方占位高度
    bottomPlaceholderHeight() {
      const remainingItems = this.items.length - this.endIndex
      return Math.max(0, remainingItems * this.itemHeight)
    },
    
    // 总高度
    totalHeight() {
      return this.items.length * this.itemHeight
    }
  },
  
  watch: {
    items: {
      handler() {
        this.updateVisibleRange()
      },
      deep: true
    },
    
    containerHeight() {
      this.updateVisibleRange()
    }
  },
  
  mounted() {
    this.$nextTick(() => {
      this.initContainer()
      this.updateVisibleRange()
    })
  },
  
  methods: {
    initContainer() {
      // 获取容器高度
      if (typeof this.height === 'string' && this.height.includes('vh')) {
        const vh = parseFloat(this.height.replace('vh', ''))
        this.containerHeight = uni.getSystemInfoSync().windowHeight * vh / 100
      } else if (typeof this.height === 'number') {
        this.containerHeight = this.height
      } else {
        this.containerHeight = 500 // 默认高度
      }
    },
    
    updateVisibleRange() {
      const scrollTop = this.scrollTop
      const containerHeight = this.containerHeight
      
      // 计算开始和结束索引
      const startIndex = Math.floor(scrollTop / this.itemHeight)
      const endIndex = Math.min(
        this.items.length,
        Math.ceil((scrollTop + containerHeight) / this.itemHeight) + this.bufferSize
      )
      
      this.startIndex = Math.max(0, startIndex - this.bufferSize)
      this.endIndex = endIndex
    },
    
    getItemKey(item, index) {
      // 如果item有唯一ID，使用ID作为key
      if (item && typeof item === 'object') {
        return item.id || item.key || index
      }
      return index
    },
    
    onScroll(e) {
      const { scrollTop } = e.detail
      this.scrollTop = scrollTop
      this.updateVisibleRange()
      this.$emit('scroll', e)
    },
    
    onScrollToUpper(e) {
      this.$emit('refresh')
    },
    
    onScrollToLower(e) {
      if (this.hasMore && !this.loading) {
        this.$emit('loadmore')
      }
    },
    
    // 滚动到指定位置
    scrollToIndex(index, animated = true) {
      const scrollTop = index * this.itemHeight
      this.scrollTop = scrollTop
      
      if (animated) {
        this.$nextTick(() => {
          this.updateVisibleRange()
        })
      } else {
        this.updateVisibleRange()
      }
    },
    
    // 滚动到顶部
    scrollToTop(animated = true) {
      this.scrollToIndex(0, animated)
    },
    
    // 滚动到底部
    scrollToBottom(animated = true) {
      const lastIndex = Math.max(0, this.items.length - 1)
      this.scrollToIndex(lastIndex, animated)
    }
  }
}
</script>

<style lang="scss" scoped>
.virtual-list {
  position: relative;
  width: 100%;
  overflow: hidden;
}

.virtual-list-item {
  width: 100%;
}

.load-more {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32rpx;
  min-height: 80rpx;
}

.load-more-text {
  margin-left: 16rpx;
  font-size: 28rpx;
  color: #999;
}
</style> 